/* 
 * File:   Repositorio.cpp
 * Author: gabilan
 * 
 * Created on 24 de mayo de 2013, 19:33
 */

#include <string>
#include <sstream>
#include "common_Repositorio.h"
#include "common_FileUtils.h"
#include "common_tinyxml.h"
#include "common_MutexScopeLocker.h"

using namespace std;
using namespace TP::Archivos;

std::string Repositorio::_rootNodeName = "Repositorio";
std::string Repositorio::_versionNodeName = "Version";
std::string Repositorio::_pathNodeName = "Path";
std::string Repositorio::_listOfRepoItemNodeName = "ListOfRepoItem";

Repositorio::Repositorio(const string &pPath) {
    _path = pPath;
    _version = VERSION_NULA;
}

Repositorio::~Repositorio() {
    if(!this->lista_archs.empty())
    {
        SetRepoItem::iterator it;
        for(it = this->lista_archs.begin(); it != this->lista_archs.end();it++)
        {
            RepoItem * r = *it;
            if(r) delete(r);
        }
        this->lista_archs.clear();
    }
}

bool Repositorio::ExisteRepoItem(const string & filename) {
    
    RepoItem* item = Primero();
    bool encontrado = false;

    while (item != NULL && !encontrado)
    {
        encontrado = (item->GetFilename() == filename);
        item = Siguiente(item);
    }
    
    return encontrado;
}

RepoItem* Repositorio::GetRepoItemByPath(const string & path) {
    
    RepoItem* item = Primero();
    bool encontrado = false;

    while (item != NULL && !encontrado)
    {
        if (item->GetFilePath() == path)
        {
            encontrado = true;
        }
        else
        {
            item = Siguiente(item);
        }
    }
    
    return item;
    
}

RepoItem* Repositorio::GetRepoItemByFilename(const string & filename) {    
    
    RepoItem* item = Primero();
    bool encontrado = false;

    while (item != NULL && !encontrado)
    {
        if (item->GetFilename() == filename)
        {
            encontrado = true;
        }
        else
        {
            item = Siguiente(item);
        }
    }
    
    return item;
}

uint32_t Repositorio::GetVersion(){
    return _version;
}

uint32_t Repositorio::SetVersion (const uint32_t &pValor){
    _version = pValor;
    return _version;
}

string Repositorio::Serializar() {
    TiXmlDocument doc;
    std::stringstream ss;
    
    TiXmlElement* rootNode = new TiXmlElement(_rootNodeName.c_str());
    
    // _version
    TiXmlElement* versionNode = new TiXmlElement(_versionNodeName.c_str());
    
    ss << _version;
    std::string versionStr(ss.str());
    ss.str("");
    
    TiXmlText* version = new TiXmlText(versionStr.c_str());        
    versionNode->LinkEndChild(version);
    
    
    // _path
    TiXmlElement* pathNode = new TiXmlElement(_pathNodeName.c_str());
    TiXmlText* path = new TiXmlText(_path.c_str());
    pathNode->LinkEndChild(path);
    
    // List of repo items
    TiXmlElement* listOfRepoItemNode = new TiXmlElement(_listOfRepoItemNodeName.c_str());
    
    RepoItem* item = Primero();
    while (NULL != item)
    {
        if (item->GetEstado() != INDEFINIDO)
        {
            TiXmlNode* repoItem = item->ToXml();
            listOfRepoItemNode->LinkEndChild(repoItem);   
        }
        
        item = Siguiente(item);
    }

    rootNode->LinkEndChild(versionNode);
    rootNode->LinkEndChild(pathNode);
    rootNode->LinkEndChild(listOfRepoItemNode);
    
    doc.LinkEndChild(rootNode);
    
    // Pasamos el doc a un string
    TiXmlPrinter printer;
    doc.Accept(&printer);
    
    std::string xmlDoc = printer.CStr();
    
    return xmlDoc;
}

Repositorio* Repositorio::Parse(const string& pXml) {
    TiXmlDocument doc;   
    doc.Parse(pXml.c_str(), 0, TIXML_ENCODING_UTF8);
    
    TiXmlNode* rootNode = doc.FirstChild(_rootNodeName.c_str());
    
    // _path
    TiXmlNode* pathNode = rootNode->FirstChild(_pathNodeName.c_str());
    std::string path = "";
    
    if (NULL != pathNode->FirstChild())
    {
        path = pathNode->FirstChild()->Value();    
    }
    
    Repositorio* newRepositorio = new Repositorio(path);
    
    // _version
    uint32_t version = 0;
    std::stringstream ss;
    
    TiXmlNode* versionNode = rootNode->FirstChild(_versionNodeName.c_str());
    TiXmlNode* versionStr = versionNode->FirstChild();
    
    if (NULL != versionStr)
    {
        ss.clear();
        ss << versionStr->Value();
        ss >> version;
        ss.str("");
    }
    
    newRepositorio->SetVersion(version);
    
    // repo items
    TiXmlNode* listOfRepoItems = rootNode->FirstChild(_listOfRepoItemNodeName.c_str());
    TiXmlNode* repoItemNode = listOfRepoItems->FirstChild();
    
    while (repoItemNode) {
        RepoItem* repoItem = RepoItem::Parse(repoItemNode);
        
        newRepositorio->AgregarRepoItem(repoItem);
        
        repoItemNode = repoItemNode->NextSibling();
    }
    
    return newRepositorio;
}

SetRepoItemIterator Repositorio::GetItemIterator() {
    return lista_archs.begin();
}

SetRepoItemIterator Repositorio::GetItemIteratorEnd(){
    return lista_archs.end();
}

RepoItem* Repositorio::CrearItem(){
    
    RepoItem* item = new RepoItem();
    
    item->SetEstado(ARCHIVO_DETECTADO);
    item->SetFilePath(_path);
    item->SetFilename(string(""));
    item->SetVersion(1);
    
    return item;
}

RepoItem* Repositorio::Primero (){

    MutexScopeLocker locker (_lista_archs_Mutex);

    RepoItem* item = NULL;
    
    if (lista_archs.size() > 0)
    {
        for (size_t i = 0; i < lista_archs.size() && item == NULL; i++)
        {
            if (lista_archs[i]->GetEstado() != INDEFINIDO)
            {
                item = lista_archs[i];
            }
        }
    }
    
    return item;
}

RepoItem* Repositorio::Siguiente (RepoItem* pRepoItem){
    
    MutexScopeLocker locker (_lista_archs_Mutex);
    
    RepoItem* item = NULL;
    SetRepoItemIterator it = lista_archs.begin();
    
    while (it < lista_archs.end() && item == NULL)
    {
        if ((*it) == pRepoItem)
        {
            it++;
            if (it < lista_archs.end())
            {
                if ((*it)->GetEstado() != INDEFINIDO)
                {
                    item = (*it);
                }
                else
                {
                    it++;
                }
            }
        }
        else
        {
            it++;
        }
    }
    
    return item;
}

void Repositorio::EliminarItem (RepoItem* pRepoItem){
    if (NULL != pRepoItem)
    {
        pRepoItem->SetEstado(INDEFINIDO);
    }
}